/*
 * Copyright (c) 2009, Servoy-stuff
 * 
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 * 
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 * http://www.fsf.org/licensing/licenses/lgpl.txt
 */
package net.stuff.servoy.plugins.listeners;

import java.awt.Component;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;

import com.servoy.j2db.scripting.IScriptObject;
import com.servoy.j2db.util.Debug;

/**
 * 
 * @author Servoy-stuff
 * http://www.servoy-stuff.net/
 */
public class KeyListenersPluginProvider implements IScriptObject {
	
	private static final String ADD_KEY_LISTENER = "addKeyListener";
	private static final String REMOVE_KEY_LISTENER = "removeKeyListener";
	private static final String REMOVE_ALL_KEY_LISTENERS = "removeAllKeyListeners";

	private static final String KEY_PRESSED = "keyPressed";
	private static final String KEY_TYPED = "keyTyped";
	private static final String KEY_RELEASED = "keyReleased";
	
	private Map functions;
	private String pluginName;
	private boolean web;
	
	public KeyListenersPluginProvider(String pluginName, boolean web) {
		this.pluginName = "plugins."+pluginName+".";
		this.web = web;
	}
	
	protected class FunctionKeyListener extends KeyAdapter {
		protected Function func;
		protected String name;
		
		public FunctionKeyListener(Function func, String name) {
			this.func = func;
			this.name = name;
		}
		
		public String getName() {
			return name;
		}
		
		protected void callBackJSFunction(KeyEvent e) {
			Context context = Context.enter();
			try {
				func.call(context, func.getParentScope(), func, new Object[] {e});
			} catch (Exception ex) {
				Debug.error(ex);
			} finally {
			     Context.exit();
			}
		}
	}
	
	protected class KeyPressedListener extends FunctionKeyListener {
		
		public KeyPressedListener(Function func, String name) {
			super(func, name);
		}
		
		/* (non-Javadoc)
		 * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
		 */
		public void keyPressed(KeyEvent e) {
			callBackJSFunction(e);
		}
	}
	
	protected class KeyReleasedListener extends FunctionKeyListener {
		
		public KeyReleasedListener(Function func, String name) {
			super(func, name);
		}
		
		/* (non-Javadoc)
		 * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
		 */
		public void keyReleased(KeyEvent e) {
			callBackJSFunction(e);
		}
	}
	
	protected class KeyTypedListener extends FunctionKeyListener {
		
		public KeyTypedListener(Function func, String name) {
			super(func, name);
		}
		
		/* (non-Javadoc)
		 * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
		 */
		public void keyTyped(KeyEvent e) {
			callBackJSFunction(e);
		}
	}
	
	public void js_addKeyListener(Object o, Function f, String eventName) {
		if (!web && o != null && o instanceof Component &&  f != null && eventName != null) {
			Component component = getTextComponent((Component)o);
			FunctionKeyListener kl = null; 
			String name = eventName+"_"+component.getName();
			if (eventName.equalsIgnoreCase(KEY_PRESSED)) {
				kl = new KeyPressedListener(f, name);
			} else if (eventName.equalsIgnoreCase(KEY_RELEASED)) {
				kl = new KeyReleasedListener(f, name);
			} else if (eventName.equalsIgnoreCase(KEY_TYPED)) {
				kl = new KeyTypedListener(f, name);
			}
			
			if (kl != null) {
				Debug.log("FunctionKeyListener created");
				
				// make sure we don't add it more than once:
				Map map = (Map) getFunctions().get(component);
				if (map != null && map.size() > 0) {
					FunctionKeyListener klBefore = (FunctionKeyListener) map.get(name);
					if (klBefore != null) {
						component.removeKeyListener(kl);
						Debug.log("FunctionKeyListener existed before");
					}
				}
				if (map == null) {
					map = new HashMap();
					map.put(name, kl);
				}
				getFunctions().put(component, map);
				
				component.addKeyListener(kl);
			}
		}
	}
	
	public void js_removeKeyListener(Object o) {
		if (!web)
			js_removeKeyListener(o, null);
	}
		
	public void js_removeKeyListener(Object o, String eventName) {
		if (!web && o != null && o instanceof Component && functions != null) {
			Component component = getTextComponent((Component)o);
			Map map = (Map) getFunctions().get(component);
			if (map != null && map.size() > 0) {
				String[] names = null;
				String name = "_"+component.getName();
				if (eventName != null) {
					names = new String[] {eventName+name};
				} else {
					names = new String[] {KEY_PRESSED+name, KEY_RELEASED+name, KEY_TYPED+name};
				}
				for (int i = 0; i < names.length; i++) {
					FunctionKeyListener fkl = (FunctionKeyListener) map.get(names[i]);
					if (fkl != null) {
						Debug.log("removed keyListener "+names[i]);
						component.removeKeyListener(fkl);
						map.remove(names[i]);
						fkl = null;
					}
				}
			}
		}
	}
	
	public void js_removeAllKeyListeners() {
		if (!web)
			js_removeAllKeyListeners(null);
	}
	
	public void js_removeAllKeyListeners(Object o) {
		if (!web && functions != null) {
			if (o != null && o instanceof Component) {
				removeAllKeyListeners(getTextComponent((Component)o));
			} else {
				List components = new ArrayList(functions.keySet());
				if (components != null) {
					for (Iterator iterator = components.iterator(); iterator.hasNext();) {
						Component component = (Component) iterator.next();
						removeAllKeyListeners(component);
					}
				}
			}
		}
	}	
	
	public Class[] getAllReturnedTypes() {
		return null;
	}

	public String[] getParameterNames(String methodName) {
		if (REMOVE_KEY_LISTENER.equals(methodName)) {
			return new String[] {"element", "[eventType]"};
		} else if (REMOVE_ALL_KEY_LISTENERS.equals(methodName)) {
			return  new String[] {"element"};
		} else if (ADD_KEY_LISTENER.equals(methodName)) {
			return new String[] {"element", "function", "eventType"};
		}
		return null;
	}

	public String getSample(String methodName) {
		if (REMOVE_KEY_LISTENER.equals(methodName)) {
			return pluginName+REMOVE_KEY_LISTENER+"(element, ['"+KEY_PRESSED+"']);";
		} else if (REMOVE_ALL_KEY_LISTENERS.equals(methodName)) {
			return pluginName+REMOVE_ALL_KEY_LISTENERS+"([element]);";
		} else if (ADD_KEY_LISTENER.equals(methodName)) {
			return pluginName+ADD_KEY_LISTENER+"(element, function, '"+KEY_PRESSED+"');";
		}
		return null;
	}

	public String getToolTip(String methodName) {
		return getSample(methodName);
	}

	public boolean isDeprecated(String methodName) {
		return false;
	}
	
	protected void removeAllKeyListeners(Component component) {
		KeyListener[] listeners = component.getKeyListeners();
		
		if (listeners != null && listeners.length > 0) {
			for (int i = listeners.length-1; i >=0 ; i--) {
				component.removeKeyListener(listeners[i]);
				Debug.log("removed keyListener "+component.getName());
			}
			functions.remove(component);
		}
	}

	/**
	 * Lazy initialize the Map
	 * @return
	 */
	protected Map getFunctions() {
		if (functions == null) {
			functions = new ConcurrentHashMap();
		}
		return functions;
	}
	
	private Component getTextComponent(Component o) {
		if (o instanceof JScrollPane) {
			return ((JScrollPane)o).getViewport().getView();
		} else if (o instanceof JComboBox) {
			return ((JComboBox)o).getEditor().getEditorComponent();
		} else if (o instanceof JPanel) {
			Component c = ((JPanel)o).getComponent(0);
			return (c != null) ? c : o;
		}
		return o;
	}

}
